package com.wpw.dream.thread;

import java.util.Date;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * DelayQueue是一个无界的BlockingQueue，用于存储实现了Delayed接口的对象，其中的对象只能在其到期时才能从队列中取走。这种队列是有序的，即队头对象的延迟到期时间最长。注意：不能将null元素放置到这种队列中。
 * 
 * Project Name：dream-test
 * ClassName：DelayedQueueDemo
 * Description：
 * @author: wupengwei
 * @date: 2018年2月8日 下午5:19:59
 * note:
 *
 */
public class DelayedQueueDemo {

	public static void main(String[] args) throws Exception {
		DelayQueue<DelayedQueueEvent> queue = new DelayQueue<>();
		for (int i = 0; i < 10; i++) {
			DelayedQueueEvent event = new DelayedQueueEvent(System.currentTimeMillis() + (i+1) * 5000, String.valueOf(i));
			queue.add(event);
		}
		System.out.println("当前时间:"+ new Date());
		
		DelayedQueueEvent event;
		while (queue.size() != 0) {
			event = queue.poll();
			if (event != null) {
				System.out.println("取出" + event.getName() + ":" + new Date());
			}
		}
		
	}
}

/**
 * DelayQueue需要存储的对象的类，实现Delayed接口，重写compareTo和getDelay方法
 */
class DelayedQueueEvent implements Delayed {
	private Long startDate;
	
	private String name;

	public DelayedQueueEvent(Long startDate, String name) {
		this.startDate = startDate;
		this.name = name;
	}

	public String getName() {
		return this.name;
	}

	//添加的元素为什么需要排序呢？查看源码可知，原因是DelayQueue采用了PriorityQueue来存储数据，而PriorityQueue所存储的元素是有序的。
	//此接口的实现必须定义一个compareTo方法，该方法提供与此接口的 getDelay方法一致的排序。
	//从DelayQueue获取元素时候，是获取PriorityQueue的头元素，并判断是否到达执行的时间，如果没到执行时间，返回null。如果 compareTo提供的排序和getDelay不一致，那么该处就会有问题，就会出现到达执行时间的元素没有被取出。
	@Override
	public int compareTo(Delayed o) {
		long result = this.getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
		if (result < 0) {
			return -1;
		} else if (result > 0) {
			return 1;
		}
		return 0;
	}

	//返回与此对象相关的剩余延迟时间，以给定的时间单位表示, 查看源码可知DelayQueue给的单位是纳秒.
	@Override
	public long getDelay(TimeUnit unit) {
		long diff = startDate - System.currentTimeMillis();
		return unit.convert(diff, TimeUnit.MILLISECONDS);
	}
}

